﻿// --------------------------------------------------------------------------------------------------------------------
// <copyright file="StrUtil.cs" company="">
//   
// </copyright>
// <summary>
//   Character stream class.
// </summary>
// --------------------------------------------------------------------------------------------------------------------
namespace KeePassLib.Utility
{
    using System;
    using System.Collections;
    using System.Collections.Generic;
    using System.Diagnostics;
    using System.Globalization;
    using System.IO;
    using System.Text;
    using System.Text.RegularExpressions;

    using KeePassLib.Collections;
    using KeePassLib.Security;

    /// <summary>
    /// Character stream class.
    /// </summary>
    public sealed class CharStream
    {
        #region Fields

        /// <summary>
        /// The m_n pos.
        /// </summary>
        private int m_nPos = 0;

        /// <summary>
        /// The m_str string.
        /// </summary>
        private string m_strString = string.Empty;

        #endregion

        #region Constructors and Destructors

        /// <summary>
        /// Initializes a new instance of the <see cref="CharStream"/> class.
        /// </summary>
        /// <param name="str">
        /// The str.
        /// </param>
        /// <exception cref="ArgumentNullException">
        /// </exception>
        public CharStream(string str)
        {
            Debug.Assert(str != null);
            if (str == null)
            {
                throw new ArgumentNullException("str");
            }

            this.m_strString = str;
        }

        #endregion

        #region Public Methods and Operators

        /// <summary>
        /// The peek char.
        /// </summary>
        /// <returns>
        /// The <see cref="char"/>.
        /// </returns>
        public char PeekChar()
        {
            if (this.m_nPos < 0)
            {
                return char.MinValue;
            }

            if (this.m_nPos >= this.m_strString.Length)
            {
                return char.MinValue;
            }

            return this.m_strString[this.m_nPos];
        }

        /// <summary>
        /// The peek char.
        /// </summary>
        /// <param name="bSkipWhiteSpace">
        /// The b skip white space.
        /// </param>
        /// <returns>
        /// The <see cref="char"/>.
        /// </returns>
        public char PeekChar(bool bSkipWhiteSpace)
        {
            if (bSkipWhiteSpace == false)
            {
                return this.PeekChar();
            }

            int iIndex = this.m_nPos;
            while (true)
            {
                if (iIndex < 0)
                {
                    return char.MinValue;
                }

                if (iIndex >= this.m_strString.Length)
                {
                    return char.MinValue;
                }

                char ch = this.m_strString[iIndex];

                if ((ch != ' ') && (ch != '\t') && (ch != '\r') && (ch != '\n'))
                {
                    return ch;
                }

                ++iIndex;
            }
        }

        /// <summary>
        /// The read char.
        /// </summary>
        /// <returns>
        /// The <see cref="char"/>.
        /// </returns>
        public char ReadChar()
        {
            if (this.m_nPos < 0)
            {
                return char.MinValue;
            }

            if (this.m_nPos >= this.m_strString.Length)
            {
                return char.MinValue;
            }

            char chRet = this.m_strString[this.m_nPos];
            ++this.m_nPos;
            return chRet;
        }

        /// <summary>
        /// The read char.
        /// </summary>
        /// <param name="bSkipWhiteSpace">
        /// The b skip white space.
        /// </param>
        /// <returns>
        /// The <see cref="char"/>.
        /// </returns>
        public char ReadChar(bool bSkipWhiteSpace)
        {
            if (bSkipWhiteSpace == false)
            {
                return this.ReadChar();
            }

            while (true)
            {
                char ch = this.ReadChar();

                if ((ch != ' ') && (ch != '\t') && (ch != '\r') && (ch != '\n'))
                {
                    return ch;
                }
            }
        }

        /// <summary>
        /// The seek.
        /// </summary>
        /// <param name="org">
        /// The org.
        /// </param>
        /// <param name="nSeek">
        /// The n seek.
        /// </param>
        public void Seek(SeekOrigin org, int nSeek)
        {
            if (org == SeekOrigin.Begin)
            {
                this.m_nPos = nSeek;
            }
            else if (org == SeekOrigin.Current)
            {
                this.m_nPos += nSeek;
            }
            else if (org == SeekOrigin.End)
            {
                this.m_nPos = this.m_strString.Length + nSeek;
            }
        }

        #endregion
    }

    /// <summary>
    /// The str encoding type.
    /// </summary>
    public enum StrEncodingType
    {
        /// <summary>
        /// The unknown.
        /// </summary>
        Unknown = 0, 

        /// <summary>
        /// The default.
        /// </summary>
        Default, 

        /// <summary>
        /// The ascii.
        /// </summary>
        Ascii, 

        /// <summary>
        /// The utf 7.
        /// </summary>
        Utf7, 

        /// <summary>
        /// The utf 8.
        /// </summary>
        Utf8, 

        /// <summary>
        /// The utf 16 le.
        /// </summary>
        Utf16LE, 

        /// <summary>
        /// The utf 16 be.
        /// </summary>
        Utf16BE, 

        /// <summary>
        /// The utf 32 le.
        /// </summary>
        Utf32LE, 

        /// <summary>
        /// The utf 32 be.
        /// </summary>
        Utf32BE
    }

    /// <summary>
    /// The str encoding info.
    /// </summary>
    public sealed class StrEncodingInfo
    {
        #region Fields

        /// <summary>
        /// The m_cb code point.
        /// </summary>
        private readonly uint m_cbCodePoint;

        /// <summary>
        /// The m_enc.
        /// </summary>
        private readonly Encoding m_enc;

        /// <summary>
        /// The m_str name.
        /// </summary>
        private readonly string m_strName;

        /// <summary>
        /// The m_type.
        /// </summary>
        private readonly StrEncodingType m_type;

        /// <summary>
        /// The m_v sig.
        /// </summary>
        private readonly byte[] m_vSig;

        #endregion

        #region Constructors and Destructors

        /// <summary>
        /// Initializes a new instance of the <see cref="StrEncodingInfo"/> class.
        /// </summary>
        /// <param name="t">
        /// The t.
        /// </param>
        /// <param name="strName">
        /// The str name.
        /// </param>
        /// <param name="enc">
        /// The enc.
        /// </param>
        /// <param name="cbCodePoint">
        /// The cb code point.
        /// </param>
        /// <param name="vStartSig">
        /// The v start sig.
        /// </param>
        /// <exception cref="ArgumentNullException">
        /// </exception>
        /// <exception cref="ArgumentOutOfRangeException">
        /// </exception>
        public StrEncodingInfo(StrEncodingType t, string strName, Encoding enc, uint cbCodePoint, byte[] vStartSig)
        {
            if (strName == null)
            {
                throw new ArgumentNullException("strName");
            }

            if (enc == null)
            {
                throw new ArgumentNullException("enc");
            }

            if (cbCodePoint <= 0)
            {
                throw new ArgumentOutOfRangeException("cbCodePoint");
            }

            this.m_type = t;
            this.m_strName = strName;
            this.m_enc = enc;
            this.m_cbCodePoint = cbCodePoint;
            this.m_vSig = vStartSig;
        }

        #endregion

        #region Public Properties

        /// <summary>
        /// Size of a character in bytes.
        /// </summary>
        public uint CodePointSize
        {
            get
            {
                return this.m_cbCodePoint;
            }
        }

        /// <summary>
        /// Gets the encoding.
        /// </summary>
        public Encoding Encoding
        {
            get
            {
                return this.m_enc;
            }
        }

        /// <summary>
        /// Gets the name.
        /// </summary>
        public string Name
        {
            get
            {
                return this.m_strName;
            }
        }

        /// <summary>
        /// Start signature of the text (byte order mark).
        /// May be <c>null</c> or empty, if no signature is known.
        /// </summary>
        public byte[] StartSignature
        {
            get
            {
                return this.m_vSig;
            }
        }

        /// <summary>
        /// Gets the type.
        /// </summary>
        public StrEncodingType Type
        {
            get
            {
                return this.m_type;
            }
        }

        #endregion
    }

    /// <summary>
    /// A class containing various string helper methods.
    /// </summary>
    public static class StrUtil
    {
        /// <summary>
        /// The case ignore cmp.
        /// </summary>
        public const StringComparison CaseIgnoreCmp = StringComparison.OrdinalIgnoreCase;

        /// <summary>
        /// Gets the case ignore comparer.
        /// </summary>
        public static StringComparer CaseIgnoreComparer
        {
            get
            {
                return StringComparer.OrdinalIgnoreCase;
            }
        }

        /// <summary>
        /// The m_b rtl.
        /// </summary>
        private static bool m_bRtl = false;

        /// <summary>
        /// Gets or sets a value indicating whether right to left.
        /// </summary>
        public static bool RightToLeft
        {
            get
            {
                return m_bRtl;
            }

            set
            {
                m_bRtl = value;
            }
        }

        /// <summary>
        /// The m_enc utf 8.
        /// </summary>
        private static UTF8Encoding m_encUtf8 = null;

        /// <summary>
        /// Gets the utf 8.
        /// </summary>
        public static UTF8Encoding Utf8
        {
            get
            {
                if (m_encUtf8 == null)
                {
                    m_encUtf8 = new UTF8Encoding(false, false);
                }

                return m_encUtf8;
            }
        }

        /// <summary>
        /// The m_l encs.
        /// </summary>
        private static List<StrEncodingInfo> m_lEncs = null;

        /// <summary>
        /// Gets the encodings.
        /// </summary>
        public static IEnumerable<StrEncodingInfo> Encodings
        {
            get
            {
                if (m_lEncs != null)
                {
                    return m_lEncs;
                }

                var l = new List<StrEncodingInfo>();

                l.Add(new StrEncodingInfo(StrEncodingType.Utf8, "Unicode (UTF-8)", StrUtil.Utf8, 1, new byte[] { 0xEF, 0xBB, 0xBF }));
                l.Add(
                    new StrEncodingInfo(
                        StrEncodingType.Utf16LE, 
                        "Unicode (UTF-16 LE)", 
                        new UnicodeEncoding(false, false), 
                        2, 
                        new byte[] { 0xFF, 0xFE }));
                l.Add(
                    new StrEncodingInfo(
                        StrEncodingType.Utf16BE, 
                        "Unicode (UTF-16 BE)", 
                        new UnicodeEncoding(true, false), 
                        2, 
                        new byte[] { 0xFE, 0xFF }));

                m_lEncs = l;
                return l;
            }
        }

        // public static string RtfPar
        // {
        // 	// get { return (m_bRtl ? "\\rtlpar " : "\\par "); }
        // 	get { return "\\par "; }
        // }

        // /// <summary>
        // /// Convert a string into a valid RTF string.
        // /// </summary>
        // /// <param name="str">Any string.</param>
        // /// <returns>RTF-encoded string.</returns>
        // public static string MakeRtfString(string str)
        // {
        // 	Debug.Assert(str != null); if(str == null) throw new ArgumentNullException("str");
        // 	str = str.Replace("\\", "\\\\");
        // 	str = str.Replace("\r", string.Empty);
        // 	str = str.Replace("{", "\\{");
        // 	str = str.Replace("}", "\\}");
        // 	str = str.Replace("\n", StrUtil.RtfPar);
        // 	StringBuilder sbEncoded = new StringBuilder();
        // 	for(int i = 0; i < str.Length; ++i)
        // 	{
        // 		char ch = str[i];
        // 		if((int)ch >= 256)
        // 			sbEncoded.Append(StrUtil.RtfEncodeChar(ch));
        // 		else sbEncoded.Append(ch);
        // 	}
        // 	return sbEncoded.ToString();
        // }

        /// <summary>
        /// The rtf encode char.
        /// </summary>
        /// <param name="ch">
        /// The ch.
        /// </param>
        /// <returns>
        /// The <see cref="string"/>.
        /// </returns>
        public static string RtfEncodeChar(char ch)
        {
            // Unicode character values must be encoded using
            // 16-bit numbers (decimal); Unicode values greater
            // than 32767 must be expressed as negative numbers
            short sh = (short)ch;
            return "\\u" + sh.ToString(NumberFormatInfo.InvariantInfo) + "?";
        }

        /// <summary>
        /// Convert a string into a valid HTML sequence representing that string.
        /// </summary>
        /// <param name="str">
        /// String to convert.
        /// </param>
        /// <returns>
        /// String, HTML-encoded.
        /// </returns>
        public static string StringToHtml(string str)
        {
            Debug.Assert(str != null);
            if (str == null)
            {
                throw new ArgumentNullException("str");
            }

            str = str.Replace(@"&", @"&amp;");
            str = str.Replace(@"<", @"&lt;");
            str = str.Replace(@">", @"&gt;");
            str = str.Replace("\"", @"&quot;");
            str = str.Replace("\'", @"&#39;");

            str = NormalizeNewLines(str, false);
            str = str.Replace("\n", @"<br />" + Environment.NewLine);

            return str;
        }

        /// <summary>
        /// The xml to string.
        /// </summary>
        /// <param name="str">
        /// The str.
        /// </param>
        /// <returns>
        /// The <see cref="string"/>.
        /// </returns>
        /// <exception cref="ArgumentNullException">
        /// </exception>
        public static string XmlToString(string str)
        {
            Debug.Assert(str != null);
            if (str == null)
            {
                throw new ArgumentNullException("str");
            }

            str = str.Replace(@"&amp;", @"&");
            str = str.Replace(@"&lt;", @"<");
            str = str.Replace(@"&gt;", @">");
            str = str.Replace(@"&quot;", "\"");
            str = str.Replace(@"&#39;", "\'");

            return str;
        }

        /// <summary>
        /// The replace case insensitive.
        /// </summary>
        /// <param name="strString">
        /// The str string.
        /// </param>
        /// <param name="strFind">
        /// The str find.
        /// </param>
        /// <param name="strNew">
        /// The str new.
        /// </param>
        /// <returns>
        /// The <see cref="string"/>.
        /// </returns>
        public static string ReplaceCaseInsensitive(string strString, string strFind, string strNew)
        {
            Debug.Assert(strString != null);
            if (strString == null)
            {
                return strString;
            }

            Debug.Assert(strFind != null);
            if (strFind == null)
            {
                return strString;
            }

            Debug.Assert(strNew != null);
            if (strNew == null)
            {
                return strString;
            }

            string str = strString;

            int nPos = 0;
            while (nPos < str.Length)
            {
                nPos = str.IndexOf(strFind, nPos, StringComparison.OrdinalIgnoreCase);
                if (nPos < 0)
                {
                    break;
                }

                str = str.Remove(nPos, strFind.Length);
                str = str.Insert(nPos, strNew);

                nPos += strNew.Length;
            }

            return str;
        }

        /// <summary>
        /// Split up a command-line into application and argument.
        /// </summary>
        /// <param name="strCmdLine">
        /// Command-line to split.
        /// </param>
        /// <param name="strApp">
        /// Application path.
        /// </param>
        /// <param name="strArgs">
        /// Arguments.
        /// </param>
        public static void SplitCommandLine(string strCmdLine, out string strApp, out string strArgs)
        {
            Debug.Assert(strCmdLine != null);
            if (strCmdLine == null)
            {
                throw new ArgumentNullException("strCmdLine");
            }

            string str = strCmdLine.Trim();

            strApp = null;
            strArgs = null;

            if (str.StartsWith("\""))
            {
                int nSecond = str.IndexOf('\"', 1);
                if (nSecond >= 1)
                {
                    strApp = str.Substring(1, nSecond - 1).Trim();
                    strArgs = str.Remove(0, nSecond + 1).Trim();
                }
            }

            if (strApp == null)
            {
                int nSpace = str.IndexOf(' ');

                if (nSpace >= 0)
                {
                    strApp = str.Substring(0, nSpace);
                    strArgs = str.Remove(0, nSpace).Trim();
                }
                else
                {
                    strApp = strCmdLine;
                }
            }

            if (strApp == null)
            {
                strApp = string.Empty;
            }

            if (strArgs == null)
            {
                strArgs = string.Empty;
            }
        }

        // /// <summary>
        // /// Initialize an RTF document based on given font face and size.
        // /// </summary>
        // /// <param name="sb"><c>StringBuilder</c> to put the generated RTF into.</param>
        // /// <param name="strFontFace">Face name of the font to use.</param>
        // /// <param name="fFontSize">Size of the font to use.</param>
        // public static void InitRtf(StringBuilder sb, string strFontFace, float fFontSize)
        // {
        // 	Debug.Assert(sb != null); if(sb == null) throw new ArgumentNullException("sb");
        // 	Debug.Assert(strFontFace != null); if(strFontFace == null) throw new ArgumentNullException("strFontFace");
        // 	sb.Append("{\\rtf1");
        // 	if(m_bRtl) sb.Append("\\fbidis");
        // 	sb.Append("\\ansi\\ansicpg");
        // 	sb.Append(Encoding.Default.CodePage);
        // 	sb.Append("\\deff0{\\fonttbl{\\f0\\fswiss MS Sans Serif;}{\\f1\\froman\\fcharset2 Symbol;}{\\f2\\fswiss ");
        // 	sb.Append(strFontFace);
        // 	sb.Append(";}{\\f3\\fswiss Arial;}}");
        // 	sb.Append("{\\colortbl\\red0\\green0\\blue0;}");
        // 	if(m_bRtl) sb.Append("\\rtldoc");
        // 	sb.Append("\\deflang1031\\pard\\plain\\f2\\cf0 ");
        // 	sb.Append("\\fs");
        // 	sb.Append((int)(fFontSize * 2));
        // 	if(m_bRtl) sb.Append("\\rtlpar\\qr\\rtlch ");
        // }

        // /// <summary>
        // /// Convert a simple HTML string to an RTF string.
        // /// </summary>
        // /// <param name="strHtmlString">Input HTML string.</param>
        // /// <returns>RTF string representing the HTML input string.</returns>
        // public static string SimpleHtmlToRtf(string strHtmlString)
        // {
        // 	StringBuilder sb = new StringBuilder();
        // 	StrUtil.InitRtf(sb, "Microsoft Sans Serif", 8.25f);
        // 	sb.Append(" ");
        // 	string str = MakeRtfString(strHtmlString);
        // 	str = str.Replace("<b>", "\\b ");
        // 	str = str.Replace("</b>", "\\b0 ");
        // 	str = str.Replace("<i>", "\\i ");
        // 	str = str.Replace("</i>", "\\i0 ");
        // 	str = str.Replace("<u>", "\\ul ");
        // 	str = str.Replace("</u>", "\\ul0 ");
        // 	str = str.Replace("<br />", StrUtil.RtfPar);
        // 	sb.Append(str);
        // 	return sb.ToString();
        // }

        /// <summary>
        /// Format an exception and convert it to a string.
        /// </summary>
        /// <param name="excp">
        /// <c>Exception</c> to convert/format.
        /// </param>
        /// <returns>
        /// String representing the exception.
        /// </returns>
        public static string FormatException(Exception excp)
        {
            string strText = string.Empty;

            if (excp.Message != null)
            {
                strText += excp.Message + Environment.NewLine;
            }

#if !KeePassLibSD
            if (excp.Source != null)
            {
                strText += excp.Source + Environment.NewLine;
            }

#endif
            if (excp.StackTrace != null)
            {
                strText += excp.StackTrace + Environment.NewLine;
            }

#if !KeePassLibSD

            if (excp.Data != null)
            {
                strText += Environment.NewLine;
                foreach (DictionaryEntry de in excp.Data)
                {
                    strText += @"'" + de.Key + @"' -> '" + de.Value + @"'" + Environment.NewLine;
                }
            }

#endif

            if (excp.InnerException != null)
            {
                strText += Environment.NewLine + "Inner:" + Environment.NewLine;
                if (excp.InnerException.Message != null)
                {
                    strText += excp.InnerException.Message + Environment.NewLine;
                }

#if !KeePassLibSD
                if (excp.InnerException.Source != null)
                {
                    strText += excp.InnerException.Source + Environment.NewLine;
                }

#endif
                if (excp.InnerException.StackTrace != null)
                {
                    strText += excp.InnerException.StackTrace + Environment.NewLine;
                }

#if !KeePassLibSD

                if (excp.InnerException.Data != null)
                {
                    strText += Environment.NewLine;
                    foreach (DictionaryEntry de in excp.InnerException.Data)
                    {
                        strText += @"'" + de.Key + @"' -> '" + de.Value + @"'" + Environment.NewLine;
                    }
                }

#endif
            }

            return strText;
        }

        /// <summary>
        /// The try parse u short.
        /// </summary>
        /// <param name="str">
        /// The str.
        /// </param>
        /// <param name="u">
        /// The u.
        /// </param>
        /// <returns>
        /// The <see cref="bool"/>.
        /// </returns>
        public static bool TryParseUShort(string str, out ushort u)
        {
#if !KeePassLibSD
            return ushort.TryParse(str, out u);
#else
			try { u = ushort.Parse(str); return true; }
			catch(Exception) { u = 0; return false; }
#endif
        }

        /// <summary>
        /// The try parse int.
        /// </summary>
        /// <param name="str">
        /// The str.
        /// </param>
        /// <param name="n">
        /// The n.
        /// </param>
        /// <returns>
        /// The <see cref="bool"/>.
        /// </returns>
        public static bool TryParseInt(string str, out int n)
        {
#if !KeePassLibSD
            return int.TryParse(str, out n);
#else
			try { n = int.Parse(str); return true; }
			catch(Exception) { n = 0; }
			return false;
#endif
        }

        /// <summary>
        /// The try parse int invariant.
        /// </summary>
        /// <param name="str">
        /// The str.
        /// </param>
        /// <param name="n">
        /// The n.
        /// </param>
        /// <returns>
        /// The <see cref="bool"/>.
        /// </returns>
        public static bool TryParseIntInvariant(string str, out int n)
        {
#if !KeePassLibSD
            return int.TryParse(str, NumberStyles.Integer, NumberFormatInfo.InvariantInfo, out n);
#else
			try
			{
				n = int.Parse(str, NumberStyles.Integer, 
					NumberFormatInfo.InvariantInfo);
				return true;
			}
			catch(Exception) { n = 0; }
			return false;
#endif
        }

        /// <summary>
        /// The try parse u int.
        /// </summary>
        /// <param name="str">
        /// The str.
        /// </param>
        /// <param name="u">
        /// The u.
        /// </param>
        /// <returns>
        /// The <see cref="bool"/>.
        /// </returns>
        public static bool TryParseUInt(string str, out uint u)
        {
#if !KeePassLibSD
            return uint.TryParse(str, out u);
#else
			try { u = uint.Parse(str); return true; }
			catch(Exception) { u = 0; }
			return false;
#endif
        }

        /// <summary>
        /// The try parse u int invariant.
        /// </summary>
        /// <param name="str">
        /// The str.
        /// </param>
        /// <param name="u">
        /// The u.
        /// </param>
        /// <returns>
        /// The <see cref="bool"/>.
        /// </returns>
        public static bool TryParseUIntInvariant(string str, out uint u)
        {
#if !KeePassLibSD
            return uint.TryParse(str, NumberStyles.Integer, NumberFormatInfo.InvariantInfo, out u);
#else
			try
			{
				u = uint.Parse(str, NumberStyles.Integer, 
					NumberFormatInfo.InvariantInfo);
				return true;
			}
			catch(Exception) { u = 0; }
			return false;
#endif
        }

        /// <summary>
        /// The try parse long.
        /// </summary>
        /// <param name="str">
        /// The str.
        /// </param>
        /// <param name="n">
        /// The n.
        /// </param>
        /// <returns>
        /// The <see cref="bool"/>.
        /// </returns>
        public static bool TryParseLong(string str, out long n)
        {
#if !KeePassLibSD
            return long.TryParse(str, out n);
#else
			try { n = long.Parse(str); return true; }
			catch(Exception) { n = 0; }
			return false;
#endif
        }

        /// <summary>
        /// The try parse long invariant.
        /// </summary>
        /// <param name="str">
        /// The str.
        /// </param>
        /// <param name="n">
        /// The n.
        /// </param>
        /// <returns>
        /// The <see cref="bool"/>.
        /// </returns>
        public static bool TryParseLongInvariant(string str, out long n)
        {
#if !KeePassLibSD
            return long.TryParse(str, NumberStyles.Integer, NumberFormatInfo.InvariantInfo, out n);
#else
			try
			{
				n = long.Parse(str, NumberStyles.Integer, 
					NumberFormatInfo.InvariantInfo);
				return true;
			}
			catch(Exception) { n = 0; }
			return false;
#endif
        }

        /// <summary>
        /// The try parse u long.
        /// </summary>
        /// <param name="str">
        /// The str.
        /// </param>
        /// <param name="u">
        /// The u.
        /// </param>
        /// <returns>
        /// The <see cref="bool"/>.
        /// </returns>
        public static bool TryParseULong(string str, out ulong u)
        {
#if !KeePassLibSD
            return ulong.TryParse(str, out u);
#else
			try { u = ulong.Parse(str); return true; }
			catch(Exception) { u = 0; }
			return false;
#endif
        }

        /// <summary>
        /// The try parse u long invariant.
        /// </summary>
        /// <param name="str">
        /// The str.
        /// </param>
        /// <param name="u">
        /// The u.
        /// </param>
        /// <returns>
        /// The <see cref="bool"/>.
        /// </returns>
        public static bool TryParseULongInvariant(string str, out ulong u)
        {
#if !KeePassLibSD
            return ulong.TryParse(str, NumberStyles.Integer, NumberFormatInfo.InvariantInfo, out u);
#else
			try
			{
				u = ulong.Parse(str, NumberStyles.Integer, 
					NumberFormatInfo.InvariantInfo);
				return true;
			}
			catch(Exception) { u = 0; }
			return false;
#endif
        }

        /// <summary>
        /// The try parse date time.
        /// </summary>
        /// <param name="str">
        /// The str.
        /// </param>
        /// <param name="dt">
        /// The dt.
        /// </param>
        /// <returns>
        /// The <see cref="bool"/>.
        /// </returns>
        public static bool TryParseDateTime(string str, out DateTime dt)
        {
#if !KeePassLibSD
            return DateTime.TryParse(str, out dt);
#else
			try { dt = DateTime.Parse(str); return true; }
			catch(Exception) { dt = DateTime.MinValue; return false; }
#endif
        }

        /// <summary>
        /// The compact string 3 dots.
        /// </summary>
        /// <param name="strText">
        /// The str text.
        /// </param>
        /// <param name="nMaxChars">
        /// The n max chars.
        /// </param>
        /// <returns>
        /// The <see cref="string"/>.
        /// </returns>
        /// <exception cref="ArgumentNullException">
        /// </exception>
        /// <exception cref="ArgumentOutOfRangeException">
        /// </exception>
        public static string CompactString3Dots(string strText, int nMaxChars)
        {
            Debug.Assert(strText != null);
            if (strText == null)
            {
                throw new ArgumentNullException("strText");
            }

            Debug.Assert(nMaxChars >= 0);
            if (nMaxChars < 0)
            {
                throw new ArgumentOutOfRangeException("nMaxChars");
            }

            if (nMaxChars == 0)
            {
                return string.Empty;
            }

            if (strText.Length <= nMaxChars)
            {
                return strText;
            }

            if (nMaxChars <= 3)
            {
                return strText.Substring(0, nMaxChars);
            }

            return strText.Substring(0, nMaxChars - 3) + "...";
        }

        /// <summary>
        /// The get string between.
        /// </summary>
        /// <param name="strText">
        /// The str text.
        /// </param>
        /// <param name="nStartIndex">
        /// The n start index.
        /// </param>
        /// <param name="strStart">
        /// The str start.
        /// </param>
        /// <param name="strEnd">
        /// The str end.
        /// </param>
        /// <returns>
        /// The <see cref="string"/>.
        /// </returns>
        public static string GetStringBetween(string strText, int nStartIndex, string strStart, string strEnd)
        {
            int nTemp;
            return GetStringBetween(strText, nStartIndex, strStart, strEnd, out nTemp);
        }

        /// <summary>
        /// The get string between.
        /// </summary>
        /// <param name="strText">
        /// The str text.
        /// </param>
        /// <param name="nStartIndex">
        /// The n start index.
        /// </param>
        /// <param name="strStart">
        /// The str start.
        /// </param>
        /// <param name="strEnd">
        /// The str end.
        /// </param>
        /// <param name="nInnerStartIndex">
        /// The n inner start index.
        /// </param>
        /// <returns>
        /// The <see cref="string"/>.
        /// </returns>
        /// <exception cref="ArgumentNullException">
        /// </exception>
        public static string GetStringBetween(string strText, int nStartIndex, string strStart, string strEnd, out int nInnerStartIndex)
        {
            if (strText == null)
            {
                throw new ArgumentNullException("strText");
            }

            if (strStart == null)
            {
                throw new ArgumentNullException("strStart");
            }

            if (strEnd == null)
            {
                throw new ArgumentNullException("strEnd");
            }

            nInnerStartIndex = -1;

            int nIndex = strText.IndexOf(strStart, nStartIndex);
            if (nIndex < 0)
            {
                return string.Empty;
            }

            nIndex += strStart.Length;

            int nEndIndex = strText.IndexOf(strEnd, nIndex);
            if (nEndIndex < 0)
            {
                return string.Empty;
            }

            nInnerStartIndex = nIndex;
            return strText.Substring(nIndex, nEndIndex - nIndex);
        }

        /// <summary>
        /// Removes all characters that are not valid XML characters,
        /// according to http://www.w3.org/TR/xml/#charsets .
        /// </summary>
        /// <param name="strText">
        /// Source text.
        /// </param>
        /// <returns>
        /// Text containing only valid XML characters.
        /// </returns>
        public static string SafeXmlString(string strText)
        {
            Debug.Assert(strText != null); // No throw
            if (string.IsNullOrEmpty(strText))
            {
                return strText;
            }

            int nLength = strText.Length;
            StringBuilder sb = new StringBuilder(nLength);

            for (int i = 0; i < nLength; ++i)
            {
                char ch = strText[i];

                if (((ch >= '\u0020') && (ch <= '\uD7FF')) || (ch == '\u0009') || (ch == '\u000A') || (ch == '\u000D')
                    || ((ch >= '\uE000') && (ch <= '\uFFFD')))
                {
                    sb.Append(ch);
                }
                else if ((ch >= '\uD800') && (ch <= '\uDBFF'))
                {
                    // High surrogate
                    if ((i + 1) < nLength)
                    {
                        char chLow = strText[i + 1];
                        if ((chLow >= '\uDC00') && (chLow <= '\uDFFF'))
                        {
                            // Low sur.
                            sb.Append(ch);
                            sb.Append(chLow);
                            ++i;
                        }
                        else
                        {
                            Debug.Assert(false);
                        }
 // Low sur. invalid
                    }
                    else
                    {
                        Debug.Assert(false);
                    }
 // Low sur. missing
                }

                Debug.Assert((ch < '\uDC00') || (ch > '\uDFFF')); // Lonely low sur.
            }

            return sb.ToString();
        }

        /// <summary>
        /// The m_rx natural split.
        /// </summary>
        private static Regex m_rxNaturalSplit = null;

        /// <summary>
        /// The compare naturally.
        /// </summary>
        /// <param name="strX">
        /// The str x.
        /// </param>
        /// <param name="strY">
        /// The str y.
        /// </param>
        /// <returns>
        /// The <see cref="int"/>.
        /// </returns>
        /// <exception cref="ArgumentNullException">
        /// </exception>
        public static int CompareNaturally(string strX, string strY)
        {
            Debug.Assert(strX != null);
            if (strX == null)
            {
                throw new ArgumentNullException("strX");
            }

            Debug.Assert(strY != null);
            if (strY == null)
            {
                throw new ArgumentNullException("strY");
            }

            strX = strX.ToLower(); // Case-insensitive comparison
            strY = strY.ToLower();

            if (m_rxNaturalSplit == null)
            {
                m_rxNaturalSplit = new Regex(@"([0-9]+)", RegexOptions.None);
            }

            string[] vPartsX = m_rxNaturalSplit.Split(strX);
            string[] vPartsY = m_rxNaturalSplit.Split(strY);

            for (int i = 0; i < Math.Min(vPartsX.Length, vPartsY.Length); ++i)
            {
                string strPartX = vPartsX[i], strPartY = vPartsY[i];
                int iPartCompare;

#if KeePassLibSD
				ulong uX = 0, uY = 0;
				try
				{
					uX = ulong.Parse(strPartX);
					uY = ulong.Parse(strPartY);
					iPartCompare = uX.CompareTo(uY);
				}
				catch(Exception) { iPartCompare = strPartX.CompareTo(strPartY); }
#else
                ulong uX, uY;
                if (ulong.TryParse(strPartX, out uX) && ulong.TryParse(strPartY, out uY))
                {
                    iPartCompare = uX.CompareTo(uY);
                }
                else
                {
                    iPartCompare = strPartX.CompareTo(strPartY);
                }

#endif

                if (iPartCompare != 0)
                {
                    return iPartCompare;
                }
            }

            if (vPartsX.Length == vPartsY.Length)
            {
                return 0;
            }

            if (vPartsX.Length < vPartsY.Length)
            {
                return -1;
            }

            return 1;
        }

        /// <summary>
        /// The remove accelerator.
        /// </summary>
        /// <param name="strMenuText">
        /// The str menu text.
        /// </param>
        /// <returns>
        /// The <see cref="string"/>.
        /// </returns>
        /// <exception cref="ArgumentNullException">
        /// </exception>
        public static string RemoveAccelerator(string strMenuText)
        {
            if (strMenuText == null)
            {
                throw new ArgumentNullException("strMenuText");
            }

            string str = strMenuText;

            for (char ch = 'A'; ch <= 'Z'; ++ch)
            {
                string strEnhAcc = @"(&" + ch.ToString() + @")";
                if (str.IndexOf(strEnhAcc) >= 0)
                {
                    str = str.Replace(@" " + strEnhAcc, string.Empty);
                    str = str.Replace(strEnhAcc, string.Empty);
                }
            }

            str = str.Replace(@"&", string.Empty);

            return str;
        }

        /// <summary>
        /// The encode menu text.
        /// </summary>
        /// <param name="strText">
        /// The str text.
        /// </param>
        /// <returns>
        /// The <see cref="string"/>.
        /// </returns>
        /// <exception cref="ArgumentNullException">
        /// </exception>
        public static string EncodeMenuText(string strText)
        {
            if (strText == null)
            {
                throw new ArgumentNullException("strText");
            }

            return strText.Replace(@"&", @"&&");
        }

        /// <summary>
        /// The encode tool tip text.
        /// </summary>
        /// <param name="strText">
        /// The str text.
        /// </param>
        /// <returns>
        /// The <see cref="string"/>.
        /// </returns>
        /// <exception cref="ArgumentNullException">
        /// </exception>
        public static string EncodeToolTipText(string strText)
        {
            if (strText == null)
            {
                throw new ArgumentNullException("strText");
            }

            return strText.Replace(@"&", @"&&&");
        }

        /// <summary>
        /// The is hex string.
        /// </summary>
        /// <param name="str">
        /// The str.
        /// </param>
        /// <param name="bStrict">
        /// The b strict.
        /// </param>
        /// <returns>
        /// The <see cref="bool"/>.
        /// </returns>
        /// <exception cref="ArgumentNullException">
        /// </exception>
        public static bool IsHexString(string str, bool bStrict)
        {
            if (str == null)
            {
                throw new ArgumentNullException("str");
            }

            if (str.Length == 0)
            {
                return true;
            }

            foreach (char ch in str)
            {
                if ((ch >= '0') && (ch <= '9'))
                {
                    continue;
                }

                if ((ch >= 'a') && (ch <= 'z'))
                {
                    continue;
                }

                if ((ch >= 'A') && (ch <= 'Z'))
                {
                    continue;
                }

                if (bStrict)
                {
                    return false;
                }

                if ((ch == ' ') || (ch == '\t') || (ch == '\r') || (ch == '\n'))
                {
                    continue;
                }

                return false;
            }

            return true;
        }

#if !KeePassLibSD

        /// <summary>
        /// The m_v pattern parts sep.
        /// </summary>
        private static readonly char[] m_vPatternPartsSep = new[] { '*' };

        /// <summary>
        /// The simple pattern match.
        /// </summary>
        /// <param name="strPattern">
        /// The str pattern.
        /// </param>
        /// <param name="strText">
        /// The str text.
        /// </param>
        /// <param name="sc">
        /// The sc.
        /// </param>
        /// <returns>
        /// The <see cref="bool"/>.
        /// </returns>
        /// <exception cref="ArgumentNullException">
        /// </exception>
        public static bool SimplePatternMatch(string strPattern, string strText, StringComparison sc)
        {
            if (strPattern == null)
            {
                throw new ArgumentNullException("strPattern");
            }

            if (strText == null)
            {
                throw new ArgumentNullException("strText");
            }

            if (strPattern.IndexOf('*') < 0)
            {
                return strText.Equals(strPattern, sc);
            }

            string[] vPatternParts = strPattern.Split(m_vPatternPartsSep, StringSplitOptions.RemoveEmptyEntries);
            if (vPatternParts == null)
            {
                Debug.Assert(false);
                return true;
            }

            if (vPatternParts.Length == 0)
            {
                return true;
            }

            if (strText.Length == 0)
            {
                return false;
            }

            if (!strPattern.StartsWith(@"*") && !strText.StartsWith(vPatternParts[0], sc))
            {
                return false;
            }

            if (!strPattern.EndsWith(@"*") && !strText.EndsWith(vPatternParts[vPatternParts.Length - 1], sc))
            {
                return false;
            }

            int iOffset = 0;
            for (int i = 0; i < vPatternParts.Length; ++i)
            {
                string strPart = vPatternParts[i];

                int iFound = strText.IndexOf(strPart, iOffset, sc);
                if (iFound < iOffset)
                {
                    return false;
                }

                iOffset = iFound + strPart.Length;
                if (iOffset == strText.Length)
                {
                    return i == (vPatternParts.Length - 1);
                }
            }

            return true;
        }

#endif // !KeePassLibSD

        /// <summary>
        /// The string to bool.
        /// </summary>
        /// <param name="str">
        /// The str.
        /// </param>
        /// <returns>
        /// The <see cref="bool"/>.
        /// </returns>
        public static bool StringToBool(string str)
        {
            if (string.IsNullOrEmpty(str))
            {
                return false; // No assert
            }

            string s = str.Trim().ToLower();
            if (s == "true")
            {
                return true;
            }

            if (s == "yes")
            {
                return true;
            }

            if (s == "1")
            {
                return true;
            }

            if (s == "enabled")
            {
                return true;
            }

            if (s == "checked")
            {
                return true;
            }

            return false;
        }

        /// <summary>
        /// The string to bool ex.
        /// </summary>
        /// <param name="str">
        /// The str.
        /// </param>
        /// <returns>
        /// The <see cref="bool?"/>.
        /// </returns>
        public static bool? StringToBoolEx(string str)
        {
            if (string.IsNullOrEmpty(str))
            {
                return null;
            }

            string s = str.Trim().ToLower();
            if (s == "true")
            {
                return true;
            }

            if (s == "false")
            {
                return false;
            }

            return null;
        }

        /// <summary>
        /// The bool to string.
        /// </summary>
        /// <param name="bValue">
        /// The b value.
        /// </param>
        /// <returns>
        /// The <see cref="string"/>.
        /// </returns>
        public static string BoolToString(bool bValue)
        {
            return bValue ? "true" : "false";
        }

        /// <summary>
        /// The bool to string ex.
        /// </summary>
        /// <param name="bValue">
        /// The b value.
        /// </param>
        /// <returns>
        /// The <see cref="string"/>.
        /// </returns>
        public static string BoolToStringEx(bool? bValue)
        {
            if (bValue.HasValue)
            {
                return BoolToString(bValue.Value);
            }

            return "null";
        }

        /// <summary>
        /// Normalize new line characters in a string. Input strings may
        /// contain mixed new line character sequences from all commonly
        /// used operating systems (i.e. \r\n from Windows, \n from Unix
        /// and \r from Mac OS.
        /// </summary>
        /// <param name="str">
        /// String with mixed new line characters.
        /// </param>
        /// <param name="bWindows">
        /// If <c>true</c>, new line characters
        /// are normalized for Windows (\r\n); if <c>false</c>, new line
        /// characters are normalized for Unix (\n).
        /// </param>
        /// <returns>
        /// String with normalized new line characters.
        /// </returns>
        public static string NormalizeNewLines(string str, bool bWindows)
        {
            if (string.IsNullOrEmpty(str))
            {
                return str;
            }

            str = str.Replace("\r\n", "\n");
            str = str.Replace("\r", "\n");

            if (bWindows)
            {
                str = str.Replace("\n", "\r\n");
            }

            return str;
        }

        /// <summary>
        /// The m_v new line chars.
        /// </summary>
        private static char[] m_vNewLineChars = null;

        /// <summary>
        /// The normalize new lines.
        /// </summary>
        /// <param name="dict">
        /// The dict.
        /// </param>
        /// <param name="bWindows">
        /// The b windows.
        /// </param>
        public static void NormalizeNewLines(ProtectedStringDictionary dict, bool bWindows)
        {
            if (dict == null)
            {
                Debug.Assert(false);
                return;
            }

            if (m_vNewLineChars == null)
            {
                m_vNewLineChars = new[] { '\r', '\n' };
            }

            List<string> vKeys = dict.GetKeys();
            foreach (string strKey in vKeys)
            {
                ProtectedString ps = dict.Get(strKey);
                if (ps == null)
                {
                    Debug.Assert(false);
                    continue;
                }

                string strValue = ps.ReadString();
                if (strValue.IndexOfAny(m_vNewLineChars) < 0)
                {
                    continue;
                }

                dict.Set(strKey, new ProtectedString(ps.IsProtected, NormalizeNewLines(strValue, bWindows)));
            }
        }

        /// <summary>
        /// The get new line seq.
        /// </summary>
        /// <param name="str">
        /// The str.
        /// </param>
        /// <returns>
        /// The <see cref="string"/>.
        /// </returns>
        public static string GetNewLineSeq(string str)
        {
            if (str == null)
            {
                Debug.Assert(false);
                return Environment.NewLine;
            }

            int n = str.Length, nLf = 0, nCr = 0, nCrLf = 0;
            char chLast = char.MinValue;
            for (int i = 0; i < n; ++i)
            {
                char ch = str[i];

                if (ch == '\r')
                {
                    ++nCr;
                }
                else if (ch == '\n')
                {
                    ++nLf;
                    if (chLast == '\r')
                    {
                        ++nCrLf;
                    }
                }

                chLast = ch;
            }

            nCr -= nCrLf;
            nLf -= nCrLf;

            int nMax = Math.Max(nCrLf, Math.Max(nCr, nLf));
            if (nMax == 0)
            {
                return Environment.NewLine;
            }

            if (nCrLf == nMax)
            {
                return "\r\n";
            }

            return (nLf == nMax) ? "\n" : "\r";
        }

        /// <summary>
        /// The alpha numeric only.
        /// </summary>
        /// <param name="str">
        /// The str.
        /// </param>
        /// <returns>
        /// The <see cref="string"/>.
        /// </returns>
        public static string AlphaNumericOnly(string str)
        {
            if (string.IsNullOrEmpty(str))
            {
                return str;
            }

            StringBuilder sb = new StringBuilder();
            for (int i = 0; i < str.Length; ++i)
            {
                char ch = str[i];
                if (((ch >= 'a') && (ch <= 'z')) || ((ch >= 'A') && (ch <= 'Z')) || ((ch >= '0') && (ch <= '9')))
                {
                    sb.Append(ch);
                }
            }

            return sb.ToString();
        }

        /// <summary>
        /// The format data size.
        /// </summary>
        /// <param name="uBytes">
        /// The u bytes.
        /// </param>
        /// <returns>
        /// The <see cref="string"/>.
        /// </returns>
        public static string FormatDataSize(ulong uBytes)
        {
            const ulong uKB = 1024;
            const ulong uMB = uKB * uKB;
            const ulong uGB = uMB * uKB;
            const ulong uTB = uGB * uKB;

            if (uBytes == 0)
            {
                return "0 KB";
            }

            if (uBytes <= uKB)
            {
                return "1 KB";
            }

            if (uBytes <= uMB)
            {
                return (((uBytes - 1UL) / uKB) + 1UL).ToString() + " KB";
            }

            if (uBytes <= uGB)
            {
                return (((uBytes - 1UL) / uMB) + 1UL).ToString() + " MB";
            }

            if (uBytes <= uTB)
            {
                return (((uBytes - 1UL) / uGB) + 1UL).ToString() + " GB";
            }

            return (((uBytes - 1UL) / uTB) + 1UL).ToString() + " TB";
        }

        /// <summary>
        /// The format data size kb.
        /// </summary>
        /// <param name="uBytes">
        /// The u bytes.
        /// </param>
        /// <returns>
        /// The <see cref="string"/>.
        /// </returns>
        public static string FormatDataSizeKB(ulong uBytes)
        {
            const ulong uKB = 1024;

            if (uBytes == 0)
            {
                return "0 KB";
            }

            if (uBytes <= uKB)
            {
                return "1 KB";
            }

            return (((uBytes - 1UL) / uKB) + 1UL).ToString() + " KB";
        }

        /// <summary>
        /// The m_v version sep.
        /// </summary>
        private static readonly char[] m_vVersionSep = new[] { '.', ',' };

        /// <summary>
        /// The parse version.
        /// </summary>
        /// <param name="strVersion">
        /// The str version.
        /// </param>
        /// <returns>
        /// The <see cref="ulong"/>.
        /// </returns>
        public static ulong ParseVersion(string strVersion)
        {
            if (strVersion == null)
            {
                Debug.Assert(false);
                return 0;
            }

            string[] vVer = strVersion.Split(m_vVersionSep);
            if ((vVer == null) || (vVer.Length == 0))
            {
                Debug.Assert(false);
                return 0;
            }

            ushort uPart;
            StrUtil.TryParseUShort(vVer[0].Trim(), out uPart);
            ulong uVer = (ulong)uPart << 48;

            if (vVer.Length >= 2)
            {
                StrUtil.TryParseUShort(vVer[1].Trim(), out uPart);
                uVer |= (ulong)uPart << 32;
            }

            if (vVer.Length >= 3)
            {
                StrUtil.TryParseUShort(vVer[2].Trim(), out uPart);
                uVer |= (ulong)uPart << 16;
            }

            if (vVer.Length >= 4)
            {
                StrUtil.TryParseUShort(vVer[3].Trim(), out uPart);
                uVer |= (ulong)uPart;
            }

            return uVer;
        }

        /// <summary>
        /// The version to string.
        /// </summary>
        /// <param name="uVersion">
        /// The u version.
        /// </param>
        /// <returns>
        /// The <see cref="string"/>.
        /// </returns>
        public static string VersionToString(ulong uVersion)
        {
            return VersionToString(uVersion, 1U);
        }

        /// <summary>
        /// The version to string.
        /// </summary>
        /// <param name="uVersion">
        /// The u version.
        /// </param>
        /// <param name="bEnsureAtLeastTwoComp">
        /// The b ensure at least two comp.
        /// </param>
        /// <returns>
        /// The <see cref="string"/>.
        /// </returns>
        [Obsolete]
        public static string VersionToString(ulong uVersion, bool bEnsureAtLeastTwoComp)
        {
            return VersionToString(uVersion, bEnsureAtLeastTwoComp ? 2U : 1U);
        }

        /// <summary>
        /// The version to string.
        /// </summary>
        /// <param name="uVersion">
        /// The u version.
        /// </param>
        /// <param name="uMinComp">
        /// The u min comp.
        /// </param>
        /// <returns>
        /// The <see cref="string"/>.
        /// </returns>
        public static string VersionToString(ulong uVersion, uint uMinComp)
        {
            StringBuilder sb = new StringBuilder();
            uint uComp = 0;

            for (int i = 0; i < 4; ++i)
            {
                if (uVersion == 0UL)
                {
                    break;
                }

                ushort us = (ushort)(uVersion >> 48);

                if (sb.Length > 0)
                {
                    sb.Append('.');
                }

                sb.Append(us.ToString(NumberFormatInfo.InvariantInfo));
                ++uComp;

                uVersion <<= 16;
            }

            while (uComp < uMinComp)
            {
                if (sb.Length > 0)
                {
                    sb.Append('.');
                }

                sb.Append('0');
                ++uComp;
            }

            return sb.ToString();
        }

        /// <summary>
        /// The m_pb opt ent.
        /// </summary>
        private static readonly byte[] m_pbOptEnt = { 0xA5, 0x74, 0x2E, 0xEC };

        /// <summary>
        /// The serialize int array.
        /// </summary>
        /// <param name="vNumbers">
        /// The v numbers.
        /// </param>
        /// <returns>
        /// The <see cref="string"/>.
        /// </returns>
        /// <exception cref="ArgumentNullException">
        /// </exception>
        public static string SerializeIntArray(int[] vNumbers)
        {
            if (vNumbers == null)
            {
                throw new ArgumentNullException("vNumbers");
            }

            StringBuilder sb = new StringBuilder();
            for (int i = 0; i < vNumbers.Length; ++i)
            {
                if (i > 0)
                {
                    sb.Append(' ');
                }

                sb.Append(vNumbers[i].ToString(NumberFormatInfo.InvariantInfo));
            }

            return sb.ToString();
        }

        /// <summary>
        /// The deserialize int array.
        /// </summary>
        /// <param name="strSerialized">
        /// The str serialized.
        /// </param>
        /// <returns>
        /// The <see cref="int[]"/>.
        /// </returns>
        /// <exception cref="ArgumentNullException">
        /// </exception>
        public static int[] DeserializeIntArray(string strSerialized)
        {
            if (strSerialized == null)
            {
                throw new ArgumentNullException("strSerialized");
            }

            if (strSerialized.Length == 0)
            {
                return new int[0];
            }

            string[] vParts = strSerialized.Split(' ');
            int[] v = new int[vParts.Length];

            for (int i = 0; i < vParts.Length; ++i)
            {
                int n;
                if (!TryParseIntInvariant(vParts[i], out n))
                {
                    Debug.Assert(false);
                }

                v[i] = n;
            }

            return v;
        }

        /// <summary>
        /// The m_v tag sep.
        /// </summary>
        private static readonly char[] m_vTagSep = new[] { ',', ';', ':' };

        /// <summary>
        /// The tags to string.
        /// </summary>
        /// <param name="vTags">
        /// The v tags.
        /// </param>
        /// <param name="bForDisplay">
        /// The b for display.
        /// </param>
        /// <returns>
        /// The <see cref="string"/>.
        /// </returns>
        /// <exception cref="ArgumentNullException">
        /// </exception>
        public static string TagsToString(List<string> vTags, bool bForDisplay)
        {
            if (vTags == null)
            {
                throw new ArgumentNullException("vTags");
            }

            StringBuilder sb = new StringBuilder();
            bool bFirst = true;

            foreach (string strTag in vTags)
            {
                if (string.IsNullOrEmpty(strTag))
                {
                    Debug.Assert(false);
                    continue;
                }

                Debug.Assert(strTag.IndexOfAny(m_vTagSep) < 0);

                if (!bFirst)
                {
                    if (bForDisplay)
                    {
                        sb.Append(", ");
                    }
                    else
                    {
                        sb.Append(';');
                    }
                }

                sb.Append(strTag);

                bFirst = false;
            }

            return sb.ToString();
        }

        /// <summary>
        /// The string to tags.
        /// </summary>
        /// <param name="strTags">
        /// The str tags.
        /// </param>
        /// <returns>
        /// The <see cref="List"/>.
        /// </returns>
        /// <exception cref="ArgumentNullException">
        /// </exception>
        public static List<string> StringToTags(string strTags)
        {
            if (strTags == null)
            {
                throw new ArgumentNullException("strTags");
            }

            List<string> lTags = new List<string>();
            if (strTags.Length == 0)
            {
                return lTags;
            }

            string[] vTags = strTags.Split(m_vTagSep);
            foreach (string strTag in vTags)
            {
                string strFlt = strTag.Trim();
                if (strFlt.Length > 0)
                {
                    lTags.Add(strFlt);
                }
            }

            return lTags;
        }

        /// <summary>
        /// The obfuscate.
        /// </summary>
        /// <param name="strPlain">
        /// The str plain.
        /// </param>
        /// <returns>
        /// The <see cref="string"/>.
        /// </returns>
        public static string Obfuscate(string strPlain)
        {
            if (strPlain == null)
            {
                Debug.Assert(false);
                return string.Empty;
            }

            if (strPlain.Length == 0)
            {
                return string.Empty;
            }

            byte[] pb = StrUtil.Utf8.GetBytes(strPlain);

            Array.Reverse(pb);
            for (int i = 0; i < pb.Length; ++i)
            {
                pb[i] = (byte)(pb[i] ^ 0x65);
            }

#if (!KeePassLibSD && !KeePassRT)
			return Convert.ToBase64String(pb);
#else
            return Convert.ToBase64String(pb);
#endif
        }

        /// <summary>
        /// The deobfuscate.
        /// </summary>
        /// <param name="strObf">
        /// The str obf.
        /// </param>
        /// <returns>
        /// The <see cref="string"/>.
        /// </returns>
        public static string Deobfuscate(string strObf)
        {
            if (strObf == null)
            {
                Debug.Assert(false);
                return string.Empty;
            }

            if (strObf.Length == 0)
            {
                return string.Empty;
            }

            try
            {
                byte[] pb = Convert.FromBase64String(strObf);

                for (int i = 0; i < pb.Length; ++i)
                {
                    pb[i] = (byte)(pb[i] ^ 0x65);
                }

                Array.Reverse(pb);

                return StrUtil.Utf8.GetString(pb, 0, pb.Length);
            }
            catch (Exception)
            {
                Debug.Assert(false);
            }

            return string.Empty;
        }

        /// <summary>
        /// Split a string and include the separators in the splitted array.
        /// </summary>
        /// <param name="str">
        /// String to split.
        /// </param>
        /// <param name="vSeps">
        /// Separators.
        /// </param>
        /// <param name="bCaseSensitive">
        /// Specifies whether separators are
        /// matched case-sensitively or not.
        /// </param>
        /// <returns>
        /// Splitted string including separators.
        /// </returns>
        public static List<string> SplitWithSep(string str, string[] vSeps, bool bCaseSensitive)
        {
            if (str == null)
            {
                throw new ArgumentNullException("str");
            }

            if (vSeps == null)
            {
                throw new ArgumentNullException("vSeps");
            }

            List<string> v = new List<string>();
            while (true)
            {
                int minIndex = int.MaxValue, minSep = -1;
                for (int i = 0; i < vSeps.Length; ++i)
                {
                    string strSep = vSeps[i];
                    if (string.IsNullOrEmpty(strSep))
                    {
                        Debug.Assert(false);
                        continue;
                    }

                    int iIndex = bCaseSensitive ? str.IndexOf(strSep) : str.IndexOf(strSep, StrUtil.CaseIgnoreCmp);
                    if ((iIndex >= 0) && (iIndex < minIndex))
                    {
                        minIndex = iIndex;
                        minSep = i;
                    }
                }

                if (minIndex == int.MaxValue)
                {
                    break;
                }

                v.Add(str.Substring(0, minIndex));
                v.Add(vSeps[minSep]);

                str = str.Substring(minIndex + vSeps[minSep].Length);
            }

            v.Add(str);
            return v;
        }

        /// <summary>
        /// The multi to single line.
        /// </summary>
        /// <param name="strMulti">
        /// The str multi.
        /// </param>
        /// <returns>
        /// The <see cref="string"/>.
        /// </returns>
        public static string MultiToSingleLine(string strMulti)
        {
            if (strMulti == null)
            {
                Debug.Assert(false);
                return string.Empty;
            }

            if (strMulti.Length == 0)
            {
                return string.Empty;
            }

            string str = strMulti;
            str = str.Replace("\r\n", " ");
            str = str.Replace("\r", " ");
            str = str.Replace("\n", " ");

            return str;
        }

        /// <summary>
        /// The split search terms.
        /// </summary>
        /// <param name="strSearch">
        /// The str search.
        /// </param>
        /// <returns>
        /// The <see cref="List"/>.
        /// </returns>
        public static List<string> SplitSearchTerms(string strSearch)
        {
            List<string> l = new List<string>();
            if (strSearch == null)
            {
                Debug.Assert(false);
                return l;
            }

            StringBuilder sbTerm = new StringBuilder();
            bool bQuoted = false;

            for (int i = 0; i < strSearch.Length; ++i)
            {
                char ch = strSearch[i];

                if (((ch == ' ') || (ch == '\t') || (ch == '\r') || (ch == '\n')) && !bQuoted)
                {
                    if (sbTerm.Length > 0)
                    {
                        l.Add(sbTerm.ToString());
                    }

                    sbTerm.Remove(0, sbTerm.Length);
                }
                else if (ch == '\"')
                {
                    bQuoted = !bQuoted;
                }
                else
                {
                    sbTerm.Append(ch);
                }
            }

            if (sbTerm.Length > 0)
            {
                l.Add(sbTerm.ToString());
            }

            return l;
        }

        /// <summary>
        /// The compare length gt.
        /// </summary>
        /// <param name="x">
        /// The x.
        /// </param>
        /// <param name="y">
        /// The y.
        /// </param>
        /// <returns>
        /// The <see cref="int"/>.
        /// </returns>
        public static int CompareLengthGt(string x, string y)
        {
            if (x.Length == y.Length)
            {
                return 0;
            }

            return (x.Length > y.Length) ? -1 : 1;
        }

        /// <summary>
        /// The is data uri.
        /// </summary>
        /// <param name="strUri">
        /// The str uri.
        /// </param>
        /// <returns>
        /// The <see cref="bool"/>.
        /// </returns>
        public static bool IsDataUri(string strUri)
        {
            return IsDataUri(strUri, null);
        }

        /// <summary>
        /// The is data uri.
        /// </summary>
        /// <param name="strUri">
        /// The str uri.
        /// </param>
        /// <param name="strReqMimeType">
        /// The str req mime type.
        /// </param>
        /// <returns>
        /// The <see cref="bool"/>.
        /// </returns>
        public static bool IsDataUri(string strUri, string strReqMimeType)
        {
            if (strUri == null)
            {
                Debug.Assert(false);
                return false;
            }

            // strReqMimeType may be null
            const string strPrefix = "data:";
            if (!strUri.StartsWith(strPrefix, StrUtil.CaseIgnoreCmp))
            {
                return false;
            }

            int iC = strUri.IndexOf(',');
            if (iC < 0)
            {
                return false;
            }

            if (!string.IsNullOrEmpty(strReqMimeType))
            {
                int iS = strUri.IndexOf(';', 0, iC);
                int iTerm = (iS >= 0) ? iS : iC;

                string strMime = strUri.Substring(strPrefix.Length, iTerm - strPrefix.Length);
                if (!strMime.Equals(strReqMimeType, StrUtil.CaseIgnoreCmp))
                {
                    return false;
                }
            }

            return true;
        }

        /// <summary>
        /// Create a data URI (according to RFC 2397).
        /// </summary>
        /// <param name="pbData">
        /// Data to encode.
        /// </param>
        /// <param name="strMimeType">
        /// Optional MIME type. If <c>null</c>,
        /// an appropriate type is used.
        /// </param>
        /// <returns>
        /// Data URI.
        /// </returns>
        public static string DataToDataUri(byte[] pbData, string strMimeType)
        {
            if (pbData == null)
            {
                throw new ArgumentNullException("pbData");
            }

            if (strMimeType == null)
            {
                strMimeType = "application/octet-stream";
            }

#if (!KeePassLibSD && !KeePassRT)
			return ("data:" + strMimeType + ";base64," + Convert.ToBase64String(
				pbData));
#else
            return "data:" + strMimeType + ";base64," + Convert.ToBase64String(pbData);
#endif
        }

        /// <summary>
        /// Convert a data URI (according to RFC 2397) to binary data.
        /// </summary>
        /// <param name="strDataUri">
        /// Data URI to decode.
        /// </param>
        /// <returns>
        /// Decoded binary data.
        /// </returns>
        public static byte[] DataUriToData(string strDataUri)
        {
            if (strDataUri == null)
            {
                throw new ArgumentNullException("strDataUri");
            }

            if (!strDataUri.StartsWith("data:", StrUtil.CaseIgnoreCmp))
            {
                return null;
            }

            int iSep = strDataUri.IndexOf(',');
            if (iSep < 0)
            {
                return null;
            }

            string strDesc = strDataUri.Substring(5, iSep - 5);
            bool bBase64 = strDesc.EndsWith(";base64", StrUtil.CaseIgnoreCmp);

            string strData = strDataUri.Substring(iSep + 1);

            if (bBase64)
            {
                return Convert.FromBase64String(strData);
            }

            using (MemoryStream ms = new MemoryStream())
            {
                Encoding enc = Encoding.UTF8;

                string[] v = strData.Split('%');
                byte[] pb = enc.GetBytes(v[0]);
                ms.Write(pb, 0, pb.Length);
                for (int i = 1; i < v.Length; ++i)
                {
                    ms.WriteByte(Convert.ToByte(v[i].Substring(0, 2), 16));
                    pb = enc.GetBytes(v[i].Substring(2));
                    ms.Write(pb, 0, pb.Length);
                }

                pb = ms.ToArray();

                return pb;
            }
        }

        /// <summary>
        /// Remove placeholders from a string (wrapped in '{' and '}').
        /// This doesn't remove environment variables (wrapped in '%').
        /// </summary>
        /// <param name="str">
        /// The str.
        /// </param>
        /// <returns>
        /// The <see cref="string"/>.
        /// </returns>
        public static string RemovePlaceholders(string str)
        {
            if (str == null)
            {
                Debug.Assert(false);
                return string.Empty;
            }

            while (true)
            {
                int iPlhStart = str.IndexOf('{');
                if (iPlhStart < 0)
                {
                    break;
                }

                int iPlhEnd = str.IndexOf('}', iPlhStart); // '{' might be at end
                if (iPlhEnd < 0)
                {
                    break;
                }

                str = str.Substring(0, iPlhStart) + str.Substring(iPlhEnd + 1);
            }

            return str;
        }

        /// <summary>
        /// The get encoding.
        /// </summary>
        /// <param name="t">
        /// The t.
        /// </param>
        /// <returns>
        /// The <see cref="StrEncodingInfo"/>.
        /// </returns>
        public static StrEncodingInfo GetEncoding(StrEncodingType t)
        {
            foreach (StrEncodingInfo sei in StrUtil.Encodings)
            {
                if (sei.Type == t)
                {
                    return sei;
                }
            }

            return null;
        }

        /// <summary>
        /// The get encoding.
        /// </summary>
        /// <param name="strName">
        /// The str name.
        /// </param>
        /// <returns>
        /// The <see cref="StrEncodingInfo"/>.
        /// </returns>
        public static StrEncodingInfo GetEncoding(string strName)
        {
            foreach (StrEncodingInfo sei in StrUtil.Encodings)
            {
                if (sei.Name == strName)
                {
                    return sei;
                }
            }

            return null;
        }

        /// <summary>
        /// The m_v pref sep chars.
        /// </summary>
        private static string[] m_vPrefSepChars = null;

        /// <summary>
        /// The byte to safe char.
        /// </summary>
        /// <param name="bt">
        /// The bt.
        /// </param>
        /// <returns>
        /// The <see cref="char"/>.
        /// </returns>
        public static char ByteToSafeChar(byte bt)
        {
            const char chDefault = '.';

            // 00-1F are C0 control chars
            if (bt < 0x20)
            {
                return chDefault;
            }

            // 20-7F are basic Latin; 7F is DEL
            if (bt < 0x7F)
            {
                return (char)bt;
            }

            // 80-9F are C1 control chars
            if (bt < 0xA0)
            {
                return chDefault;
            }

            // A0-FF are Latin-1 supplement; AD is soft hyphen
            if (bt == 0xAD)
            {
                return '-';
            }

            return (char)bt;
        }

        /// <summary>
        /// The count.
        /// </summary>
        /// <param name="str">
        /// The str.
        /// </param>
        /// <param name="strNeedle">
        /// The str needle.
        /// </param>
        /// <returns>
        /// The <see cref="int"/>.
        /// </returns>
        public static int Count(string str, string strNeedle)
        {
            if (str == null)
            {
                Debug.Assert(false);
                return 0;
            }

            if (string.IsNullOrEmpty(strNeedle))
            {
                Debug.Assert(false);
                return 0;
            }

            int iOffset = 0, iCount = 0;
            while (iOffset < str.Length)
            {
                int p = str.IndexOf(strNeedle, iOffset);
                if (p < 0)
                {
                    break;
                }

                ++iCount;
                iOffset = p + 1;
            }

            return iCount;
        }
    }
}